__author__      = "Michael Kunkelmann"
__copyright__   = "Copyright 2017, Linde Material Handling GmbH"
__version__     = "0.0.1"
__email__       = "michael.kunkelmann@lind-mh.de"
__status__      = "Prototype"
__date__        = "xxxx-xx-xx"

import pyvxlapi
import ctypes
import struct


if struct.calcsize("P") * 8 == 32:
    VXLAPI_DLL_NAME = "vxlapi.dll"
else:
    VXLAPI_DLL_NAME = "vxlapi64.dll"


class cancardxl( ):

    def __init__( self ):
        self.vxlapidll = ctypes.windll.LoadLibrary(VXLAPI_DLL_NAME)      
            
    def load_driver( self ):
        return self.vxlapidll.xlOpenDriver()
        
    def close_driver( self ):
        return self.vxlapidll.xlCloseDriver()


    def get_appl_config( self, appName = "Default", appChannel = 0, busType = pyvxlapi.XL_BUS_TYPE_CAN ):
    
       pHWType    = ctypes.pointer( ctypes.c_uint() )
       pHWIndex   = ctypes.pointer( ctypes.c_uint() )
       pHWChannel = ctypes.pointer( ctypes.c_uint() )
       appName    = ctypes.c_char_p( appName )
       appChannel = ctypes.c_uint( appChannel )
       busType    = ctypes.c_uint( busType )
       
       #self.vxlapidll.xlGetApplConfig.argtypes[ ctypes.c_char_p,     # char          *appName
       #                                         ctypes.c_uint,       # unsigned int  appChannel, 
       #                                         pHWtype,             # unsigned int  *pHwType,
       #                                         pHWIndex,            # unsigned int  *pHwIndex, 
       #                                         pHWChannel,          # unsigned int  *pHwChannel, 
       #                                         ctypes.c_uint]       # unsigned int  busType
       status = self.vxlapidll.xlGetApplConfig( appName, appChannel, pHWType, pHWIndex, pHWChannel, busType )
       return status, pHWType.contents, pHWIndex.contents, pHWChannel.contents

    def set_appl_config( self, appName = "", appChannel = 0, HWType = 0, HWIndex = 0, HWChannel = 0, busType = pyvxlapi.XL_BUS_TYPE_CAN ):
       self.vxlapidll.xlSetApplConfig.argtypes = [ ctypes.c_char_p,    # char          *appName
                                                   ctypes.c_uint,      # unsigned int  appChannel, 
                                                   ctypes.c_uint,      # unsigned int  pHwType,
                                                   ctypes.c_uint,      # unsigned int  pHwIndex, 
                                                   ctypes.c_uint,      # unsigned int  pHwChannel, 
                                                   ctypes.c_uint ]     # unsigned int  busType
       return self.vxlapidll.xlSetApplConfig( appName, appChannel, HWType, HWIndex, HWChannel, busType )
       
    def open_port( self, port_handle, user_name, access_mask, permission_mask, rx_queue_size, interface_version, bus_type ):
        self.vxlapidll.xlOpenPort.argtypes = [ ctypes.POINTER( pyvxlapi.XLHandle ), 
                                              ctypes.c_char_p,
                                              pyvxlapi.XLAccess,
                                              ctypes.POINTER(pyvxlapi.XLAccess),
                                              ctypes.c_uint,
                                              ctypes.c_uint,
                                              ctypes.c_uint]
                                              
        status = self.vxlapidll.xlOpenPort( port_handle, 
                                            user_name, 
                                            access_mask, 
                                            permission_mask, 
                                            rx_queue_size, 
                                            interface_version, 
                                            bus_type )
        return status, port_handle, permission_mask

    def get_channel_index( self, HWType = 0, HWIndex = 0, HWChannel = 0 ):
        self.vxlapidll.xlGetChannelIndex.argtypes=[ ctypes.c_int, ctypes.c_int, ctypes.c_int ]
        return self.vxlapidll.xlGetChannelIndex( HWType, HWIndex, HWChannel )
        
    def get_channel_mask( self, HWType = 0, HWIndex = 0, HWChannel = 0 ):
        self.vxlapidll.xlGetChannelMask.argtypes = [ ctypes.c_int, ctypes.c_int, ctypes.c_int ]
        return self.vxlapidll.xlGetChannelMask( HWType, HWIndex, HWChannel)
        
    def deactivate_channel( self, port_handle, access_mask ):
        self.vxlapidll.xlDeactivateChannel.argtypes = [ pyvxlapi.XLHandle, pyvxlapi.XLAccess ]
        return self.vxlapidll.xlDeactivateChannel(port_handle, access_mask)

    def close_port( self, port_handle ):
        self.vxlapidll.xlClosePort.argtypes=[ pyvxlapi.XLHandle ]
        return self.vxlapidll.xlClosePort( port_handle )
        
    def activate_channel( self, port_handle, access_mask, BusType, flags ):
        self.vxlapidll.xlActivateChannel.argtypes = [ pyvxlapi.XLHandle,
                                                      pyvxlapi.XLAccess,
                                                      ctypes.c_uint, 
                                                      ctypes.c_uint ]
        return self.vxlapidll.xlActivateChannel( port_handle, access_mask, BusType, flags )
        
    def receive( self, port_handle, event_count, event_list ):
        self.vxlapidll.xlReceive.argtypes = [ pyvxlapi.XLHandle, ctypes.POINTER( ctypes.c_uint ), ctypes.POINTER(pyvxlapi.XLEvent)]
        return self.vxlapidll.xlReceive( port_handle, ctypes.byref(event_count), ctypes.byref(event_list))    
        
    def can_transmit( self, port_handle, mask, pmessage_count, pmessages ):
        self.vxlapidll.xlCanTransmit.argtypes = [ pyvxlapi.XLHandle, pyvxlapi.XLAccess, ctypes.POINTER(ctypes.c_uint), ctypes.c_void_p ]
        #status = self.vxlapidll.xlCanTransmit( port_handle, mask, ctypes.byref(pmessage_count), ctypes.byref(pmessages))
        status = self.vxlapidll.xlCanTransmit( port_handle, mask, pmessage_count, ctypes.byref(pmessages) )
        return status, pmessage_count, pmessages

    def get_error_description( self, error_type ):
        self.vxlapidll.xlGetErrorString.argtypes =[ pyvxlapi.XLStatus ]
        self.vxlapidll.xlGetErrorString.restype = ctypes.c_char_p
        return self.vxlapidll.xlGetErrorString( error_type )
        
    def get_event_string(self, event_type ):
        self.vxlapidll.xlGetEventString.argtypes = [ ctypes.POINTER(pyvxlapi.XLEvent) ]
        self.vxlapidll.xlGetEventString.restype = ctypes.c_char_p
        return self.vxlapidll.xlGetEventString( ctypes.pointer(event_type) )

    def can_set_channel_bitrate( self, port_handle, mask, bitrate ):
        self.vxlapidll.xlCanSetChannelBitrate.argtypes = [ pyvxlapi.XLHandle, pyvxlapi.XLAccess, ctypes.c_ulong ]
        return self.vxlapidll.xlCanSetChannelBitrate( port_handle, mask, ctypes.c_ulong(bitrate) )

        
        
class cancardxl_api():

    def __init__( self, app_name = "Default", bitrate = 500000, channel = 0  ):
        try:
            self.cancard_obj = cancardxl()
        except:
            self.cancard_obj = None

        if self.cancard_obj!=None:
            # for hw_type in [57,15,2]: # try to open different cards
            for hw_type in [55,15,2]: # try to open different cards   
            #for hw_type in [1,15,2]: # try to open different cards  Virtual          
                if False!=self.open_channel( app_name, bitrate, channel, 16, hw_type ): 
                    return 
        print("can't open cancard")
        
    def open_channel( self, 
                      app_name = "Default", 
                      bitrate = 500000, 
                      channel = 0, 
                      #rx_queue_size = 8,
                      rx_queue_size = 512, 
                      #hw_type = pyvxlapi.XL_HWTYPE_CANCARDX,
                      hw_type = pyvxlapi.XL_HWTYPE_VIRTUAL,
                      hw_index = 0 ):
                      
        self.cancard_obj.load_driver()
        self.cancard_obj.set_appl_config( app_name, channel )
              
        self.channel_mask = self.cancard_obj.get_channel_mask( hw_type, hw_index, channel )
                      
        if self.channel_mask == 0:
           return pyvxlapi.XL_ERR_HW_NOT_PRESENT
               
        tmp = self.channel_mask
        self.channel_mask    = pyvxlapi.XLuint64(tmp)
        self.channel_mask_tx = pyvxlapi.XLuint64(tmp)
        self.permission_mask = pyvxlapi.XLuint64(tmp)
        
        self.port_handle = pyvxlapi.XLHandle(pyvxlapi.XL_INVALID_PORTHANDLE)
        status, self.port_handle, permission_mask = self.cancard_obj.open_port( self.port_handle,
                                                                                app_name,
                                                                                self.channel_mask, 
                                                                                self.permission_mask,
                                                                                rx_queue_size,
                                                                                pyvxlapi.XL_INTERFACE_VERSION,
                                                                                pyvxlapi.XL_BUS_TYPE_CAN )    


        if status == pyvxlapi.XL_SUCCESS and self.port_handle != pyvxlapi.XL_INVALID_PORTHANDLE:
           if permission_mask:
              status = self.cancard_obj.can_set_channel_bitrate( self.port_handle, self.channel_mask, bitrate )

        if status != pyvxlapi.XL_SUCCESS or self.port_handle == pyvxlapi.XL_INVALID_PORTHANDLE:
           self.cancard_obj.close_port( self.port_handle )
           return pyvxlapi.XL_ERR_INVALID_HANDLE
           
        return self.cancard_obj.activate_channel( self.port_handle, 
                                                  self.channel_mask, 
                                                  pyvxlapi.XL_BUS_TYPE_CAN, 
                                                  pyvxlapi.XL_ACTIVATE_RESET_CLOCK )
        
    def print_error( self, error_type ):
        return self.cancard_obj.get_error_description( error_type )
        
    def close_channel( self ):
        self.cancard_obj.deactivate_channel( self.port_handle, self.channel_mask )
        self.cancard_obj.close_port( self.port_handle )
        self.cancard_obj.close_driver()
        
    def send_msg( self, data, id, debug = False ):
        xlevent = pyvxlapi.XLEvent(0)
        xlevent.tag = pyvxlapi.XL_TRANSMIT_MSG
        xlevent.tagData.msg.id = id
        xlevent.tagData.msg.flags = 0
        dlc = len( data )

        if debug:            
            msg_debug_out = ""
        
        xlevent.tagData.msg.dlc = dlc
        for i in range(0, dlc):          
            xlevent.tagData.msg.data[i] = data[i]
            if debug:
               msg_debug_out += "%02X " % data[i]
               
        if debug:
            pass
            #print "TX -> " + msg_debug_out

        return self.cancard_obj.can_transmit( self.port_handle, self.channel_mask_tx, ctypes.c_uint(1), xlevent )

    def get_msg( self ):
        event_count = ctypes.c_uint(1)
        event_list  = pyvxlapi.XLEvent(1)    
        status = self.cancard_obj.receive( self.port_handle, event_count, event_list )

        if status == pyvxlapi.XL_SUCCESS:
           return 0, event_list #self.cancard_obj.get_event_string( event_list )
        return 1, self.cancard_obj.get_error_description( status )

    def handle_response( self, status ):
        rv = True
        if status != pyvxlapi.XL_SUCCESS:
           if status == pyvxlapi.XL_ERR_HW_NOT_PRESENT:
              print ("Hardware not present!")
           elif status == pyvxlapi.XL_ERR_INVALID_HANDLE:
              print ("Invalid Handle!")
           else:
              print ("Unspecified error: "), status
           rv = False
        return rv
        